﻿using System.Text;

namespace KinonekoSoftware.CodariaDB.IO.Formats
{
    public abstract class FileFormat
    {
        public const ulong TextFormatPrefix   = 0x547970652e547874UL;
        public const ulong BinaryFormatPrefix = 0x547970652e42696eUL;

        public static async Task<Result<BinaryFormat<T>>> ReadBinary<T>(byte[] buffer, BinaryFormatBuilder<T> builder, uint? specVersion = null, uint? specType = null)
        {
            if (buffer is null)
            {
                return Result<BinaryFormat<T>>.Failed("数据为空");
            }

            if (builder is null)
            {
                return Result<BinaryFormat<T>>.Failed($"缺少{typeof(T)}类型的反序列化器");
            }

            if (buffer.Length < 12)
            {
                return Result<BinaryFormat<T>>.Failed($"这是不正确的文件格式或者文件已损坏，正确的文件大小应该大于16字节，此文件大小为:{buffer.Length}");
            }

            //
            //
            var prefix  = BitConverter.ToUInt64(buffer, 0);
            var version = BitConverter.ToUInt32(buffer, 8);
            var type    = BitConverter.ToUInt32(buffer, 12);

            if (prefix != BinaryFormatPrefix)
            {
                return Result<BinaryFormat<T>>.Failed($"这是不正确的文件格式，此格式要求文件前缀为:{BinaryFormatPrefix:x}，但实际的前缀为{prefix:x}");
            }

            if (specVersion.HasValue && type != specVersion.Value)
            {
                return Result<BinaryFormat<T>>.Failed($"这是不正确的文件格式，此格式要求文件前缀为:{specVersion:x}，但实际的前缀为{version:x}");
            }

            if (specType.HasValue && type != specType.Value)
            {
                return Result<BinaryFormat<T>>.Failed($"这是不正确的文件格式，此格式要求文件前缀为:{specType:x}，但实际的前缀为{type:x}");
            }

            try
            {
                var format = await builder.Build(version, type, buffer);
                return Result<BinaryFormat<T>>.Success(format);
            }
            catch(Exception ex)
            {
                return Result<BinaryFormat<T>>.Failed(ex.Message);
            }
        }


        public static async Task<Result<BinaryFormat<T>>> ReadBinary<T>(Stream buffer, BinaryFormatBuilder<T> builder, uint? specVersion = null, uint? specType = null)
        {
            if (buffer is null)
            {
                return Result<BinaryFormat<T>>.Failed("数据为空");
            }

            if (builder is null)
            {
                return Result<BinaryFormat<T>>.Failed($"缺少{typeof(T)}类型的反序列化器");
            }

            if (buffer.Length < 12)
            {
                return Result<BinaryFormat<T>>.Failed($"这是不正确的文件格式或者文件已损坏，正确的文件大小应该大于16字节，此文件大小为:{buffer.Length}");
            }

            using (var br = new BinaryReader(buffer))
            {

                //
                //
                var prefix  = br.ReadUInt64();
                var version = br.ReadUInt32();
                var type    = br.ReadUInt32();

                if (prefix != BinaryFormatPrefix)
                {
                    return Result<BinaryFormat<T>>.Failed($"这是不正确的文件格式，此格式要求文件前缀为:{BinaryFormatPrefix:x}，但实际的前缀为{prefix:x}");
                }

                if (specVersion.HasValue && type != specVersion.Value)
                {
                    return Result<BinaryFormat<T>>.Failed($"这是不正确的文件格式，此格式要求文件前缀为:{specVersion:x}，但实际的前缀为{version:x}");
                }

                if (specType.HasValue && type != specType.Value)
                {
                    return Result<BinaryFormat<T>>.Failed($"这是不正确的文件格式，此格式要求文件前缀为:{specType:x}，但实际的前缀为{type:x}");
                }

                try
                {
                    var format = await builder.Build(version, type, buffer);
                    return Result<BinaryFormat<T>>.Success(format);
                }
                catch(Exception ex)
                {
                    return Result<BinaryFormat<T>>.Failed(ex.Message);
                }
            }
        }


        public static Result<TextFormat<T>> ReadText<T>(byte[] buffer, uint specVersion, uint specType) where T : class
        {
            if (buffer is null)
            {
                return Result<TextFormat<T>>.Failed("数据为空");
            }

            if (buffer.Length < 12)
            {
                return Result<TextFormat<T>>.Failed($"这是不正确的文件格式或者文件已损坏，正确的文件大小应该大于16字节，此文件大小为:{buffer.Length}");
            }

            //
            //
            var prefix  = BitConverter.ToUInt64(buffer, 0);
            var version = BitConverter.ToUInt32(buffer, 8);
            var type    = BitConverter.ToUInt32(buffer, 12);

            if (prefix != TextFormatPrefix)
            {
                return Result<TextFormat<T>>.Failed($"这是不正确的文件格式，此格式要求文件前缀为:{TextFormatPrefix:x}，但实际的前缀为{prefix:x}");
            }

            if (type != specVersion)
            {
                return Result<TextFormat<T>>.Failed($"这是不正确的文件格式，此格式要求文件前缀为:{specVersion:x}，但实际的前缀为{version:x}");
            }

            if (type != specType)
            {
                return Result<TextFormat<T>>.Failed($"这是不正确的文件格式，此格式要求文件前缀为:{specType:x}，但实际的前缀为{type:x}");
            }

            try
            {
                var payload  = Encoding.UTF8.GetString(buffer.AsSpan()[16..]);
                var document = JSON.Document<T>(payload);
                return Result<TextFormat<T>>.Success(new TextFormat<T>
                {
                    Type    = type,
                    Version = version,
                    Value   = document,
                });
            }
            catch(Exception ex)
            {
                return Result<TextFormat<T>>.Failed(ex.Message);
            }
        }


        public static Result<TextFormat<T>> ReadText<T>(Stream stream, uint specVersion, uint specType) where T : class
        {
            if (stream is null)
            {
                return Result<TextFormat<T>>.Failed("数据为空");
            }

            if (stream.Length < 12)
            {
                return Result<TextFormat<T>>.Failed($"这是不正确的文件格式或者文件已损坏，正确的文件大小应该大于16字节，此文件大小为:{stream.Length}");
            }

            using (var br = new BinaryReader(stream))
            {

                //
                //
                var prefix  = br.ReadUInt64();
                var version = br.ReadUInt32();
                var type    = br.ReadUInt32();

                if (prefix != TextFormatPrefix)
                {
                    return Result<TextFormat<T>>.Failed($"这是不正确的文件格式，此格式要求文件前缀为:{TextFormatPrefix:x}，但实际的前缀为{prefix:x}");
                }

                if (type != specVersion)
                {
                    return Result<TextFormat<T>>.Failed($"这是不正确的文件格式，此格式要求文件前缀为:{specVersion:x}，但实际的前缀为{version:x}");
                }

                if (type != specType)
                {
                    return Result<TextFormat<T>>.Failed($"这是不正确的文件格式，此格式要求文件前缀为:{specType:x}，但实际的前缀为{type:x}");
                }

                try
                {
                    var buffer   = br.ReadBytes((int)(stream.Length - 16));
                    var payload  = Encoding.UTF8.GetString(buffer);
                    var document = JSON.Document<T>(payload);
                    return Result<TextFormat<T>>.Success(new TextFormat<T>
                    {
                        Type    = type,
                        Version = version,
                        Value   = document,
                    });
                }
                catch(Exception ex)
                {
                    return Result<TextFormat<T>>.Failed(ex.Message);
                }
            }
        }

        public static async Task WriteText<T>(string fileName, uint version, uint type, T instance) where T : class
        {
            await using (var stream = File.Open(fileName, FileMode.Create))
            {
                await using (var bw = new BinaryWriter(stream))
                {
                    bw.Write(TextFormatPrefix);
                    bw.Write(version);
                    bw.Write(type);
                    bw.Write(JSON.Dump(instance));
                }
            }
        }

        public uint Version { get; set; }
        public uint Type    { get; set; }
    }
}